import random
from .game_mode import RobotDifficulty
from .player import Player

try:
    from .llm_agent import GomokuLLMAgent
except Exception:
    GomokuLLMAgent = None


class Robot:
    def __init__(self, difficulty=RobotDifficulty.MEDIUM):
        self.difficulty = difficulty
        self.llm_agent = None

        # 置换表缓存，用于存储已评估的局面分数
        self.transposition_table = {}
        self.cache_size_limit = 10000  # 限制缓存大小

        if self.difficulty == RobotDifficulty.OPENAI and GomokuLLMAgent:
            try:
                self.llm_agent = GomokuLLMAgent()
            except Exception as exc:
                print(f"AI大模型初始化失败，回退至困难策略：{exc}")
                self.llm_agent = None
        elif self.difficulty == RobotDifficulty.OPENAI and not GomokuLLMAgent:
            print("未找到大模型依赖，回退至困难策略")

    def get_best_move(self, board):
        # 选择落子位置
        if self.difficulty == RobotDifficulty.EASY:
            return self._get_random_move(board)
        elif self.difficulty == RobotDifficulty.MEDIUM:
            return self._get_medium_move(board)
        elif self.difficulty == RobotDifficulty.OPENAI:
            return self._get_llm_move(board)
        else:  # HARD
            return self._get_hard_move(board)

    def _get_random_move(self, board):
        # 随机找个空位下棋
        available_moves = []
        for row in range(board.size):
            for col in range(board.size):
                if board.is_valid_move(row, col):
                    available_moves.append((row, col))

        if available_moves:
            return random.choice(available_moves)
        return None

    def _get_medium_move(self, board):
        # 用简单评分选位置
        # 首先检查是否有一步必胜的位置
        winning_move = self._find_winning_move(board, Player.WHITE.value)
        if winning_move:
            return winning_move

        # 检查是否需要阻止对手获胜
        blocking_move = self._find_winning_move(board, Player.BLACK.value)
        if blocking_move:
            return blocking_move

        best_score = -1
        best_move = None

        # 获取有效的落子点（周围有子的地方优先考虑）
        candidate_moves = self._get_candidate_moves(board)

        for row, col in candidate_moves:
            if board.is_valid_move(row, col):
                score = self._evaluate_position(board, row, col)
                if score > best_score:
                    best_score = score
                    best_move = (row, col)

        return best_move if best_move else self._get_random_move(board)

    def _get_hard_move(self, board):
        # 用算法选最好的位置
        # 首先检查是否有一步必胜的位置
        winning_move = self._find_winning_move(board, Player.WHITE.value)
        if winning_move:
            return winning_move

        # 检查是否需要阻止对手获胜
        blocking_move = self._find_winning_move(board, Player.BLACK.value)
        if blocking_move:
            return blocking_move

        best_score = float('-inf')
        best_move = None

        candidate_moves = self._get_candidate_moves(board)

        for row, col in candidate_moves:
            if board.is_valid_move(row, col):
                # 临时放置棋子
                board.place_stone(row, col, Player.WHITE.value)

                # 优化的动态深度策略 - 平衡性能与棋力
                piece_count = sum(1 for i in range(board.size) for j in range(board.size) if board.board[i][j] != 0)

                # 计算当前位置的基础评分，判断重要性
                position_score = self._quick_evaluate_position(board, row, col)

                if piece_count < 8:
                    # 开局阶段：深度2（原来3），大幅减少计算量
                    depth = 2
                elif piece_count < 20:
                    # 中局前期：深度2-3，根据位置重要性决定
                    if position_score > 5000:  # 关键位置（如活三冲四）
                        depth = 3
                    else:
                        depth = 2
                else:
                    # 中局后期：深度2-4，只有关键位置才用深度4
                    if position_score > 10000:  # 极关键位置（如活四）
                        depth = 4
                    elif position_score > 2000:  # 重要位置
                        depth = 3
                    else:
                        depth = 2

                # 确保最小深度
                depth = max(1, depth)

                # Minimax搜索
                score = self._minimax(board, depth, float('-inf'), float('inf'), False)

                # 撤销棋子
                board.undo()

                if score > best_score:
                    best_score = score
                    best_move = (row, col)

        return best_move if best_move else self._get_random_move(board)

    def _get_candidate_moves(self, board):
        # 找可以下棋的位置 - 优化版
        # 如果棋盘为空，从中心开始
        if not any(board.board[i][j] for i in range(board.size) for j in range(board.size)):
            return [(board.size // 2, board.size // 2)]

        # 找出所有已有棋子周围的空位，缩小范围到1格内
        moves_with_score = []

        for row in range(board.size):
            for col in range(board.size):
                if board.board[row][col] != 0:
                    # 检查周围的位置（1格范围内），大幅减少候选位置
                    for dr in range(-1, 2):
                        for dc in range(-1, 2):
                            if dr == 0 and dc == 0:
                                continue
                            new_row, new_col = row + dr, col + dc
                            if (0 <= new_row < board.size and
                                0 <= new_col < board.size and
                                board.board[new_row][new_col] == 0):

                                # 计算基础评分用于排序，提高剪枝效率
                                base_score = self._quick_evaluate_position(board, new_row, new_col)
                                # 距离优先级（距离越近优先级越高）
                                distance = abs(dr) + abs(dc)
                                priority = 5 - distance
                                moves_with_score.append((base_score + priority, new_row, new_col))

        # 去重，保留最高分数
        unique_moves = {}
        for score, row, col in moves_with_score:
            key = (row, col)
            if key not in unique_moves or unique_moves[key] < score:
                unique_moves[key] = score

        # 按评分排序，只取前10个候选位置（原来是20个）
        sorted_moves = sorted(unique_moves.items(), key=lambda x: x[1], reverse=True)
        candidate_moves = [pos for pos, _ in sorted_moves[:10]]

        return candidate_moves if candidate_moves else [(board.size // 2, board.size // 2)]

    def _quick_evaluate_position(self, board, row, col):
        # 快速基础评分，用于候选位置排序
        # 只计算最关键的威胁类型，避免复杂计算
        score = 0

        # 检查是否形成连子
        for player in [Player.WHITE.value, Player.BLACK.value]:
            directions = [(0, 1), (1, 0), (1, 1), (1, -1)]

            for dr, dc in directions:
                count = 1  # 包括当前位置

                # 正向计数
                r, c = row + dr, col + dc
                while 0 <= r < board.size and 0 <= c < board.size and board.board[r][c] == player:
                    count += 1
                    r += dr
                    c += dc

                # 反向计数
                r, c = row - dr, col - dc
                while 0 <= r < board.size and 0 <= c < board.size and board.board[r][c] == player:
                    count += 1
                    r -= dr
                    c -= dc

                # 基础评分权重
                if player == Player.WHITE.value:  # 进攻权重更高
                    weight = 1.2
                else:  # 防守权重
                    weight = 1.0

                # 连子评分
                if count >= 5:
                    score += 100000 * weight
                elif count == 4:
                    score += 10000 * weight
                elif count == 3:
                    score += 1000 * weight
                elif count == 2:
                    score += 100 * weight

        # 位置权重（中心位置更有价值）
        center_row, center_col = board.size // 2, board.size // 2
        distance_from_center = abs(row - center_row) + abs(col - center_col)
        position_bonus = max(0, 10 - distance_from_center)

        return score + position_bonus

    def _evaluate_position(self, board, row, col):
        # 计算这个位置好不好
        # 进攻得分
        attack_score = self._calculate_pattern_score(board, row, col, Player.WHITE.value)

        # 防守得分（检查阻止对手的价值）
        defense_score = self._calculate_pattern_score(board, row, col, Player.BLACK.value)

        # 紧急防守检测：如果对手有活三或冲四，必须防守
        critical_defense = self._check_critical_defense(board, row, col)

        # 位置权重（靠近中心的位置更有价值）
        center_row, center_col = board.size // 2, board.size // 2
        distance_from_center = abs(row - center_row) + abs(col - center_col)
        position_weight = max(0, 20 - distance_from_center * 2)

        # 防守权重提高，特别是紧急情况
        if critical_defense:
            defense_score *= 3  # 紧急防守权重大幅提升
        else:
            defense_score *= 1.5  # 一般防守权重提升

        return attack_score * 1.1 + defense_score + position_weight

    def _check_critical_defense(self, board, row, col):
        # 检查是不是必须防守
        # 临时放置对手棋子，看是否形成威胁
        temp_score = self._calculate_pattern_score(board, row, col, Player.BLACK.value)

        # 如果对手在此位置能形成活三（1000分）或冲四（5000分）以上的威胁
        return temp_score >= 1000

    def _calculate_pattern_score(self, board, row, col, player):
        # 算这个位置能得几分
        directions = [(0, 1), (1, 0), (1, 1), (1, -1)]  # 水平、垂直、两个对角线
        total_score = 0

        for dr, dc in directions:
            # 正向计数
            count_pos = 0
            r, c = row + dr, col + dc
            while (0 <= r < board.size and 0 <= c < board.size and
                   board.board[r][c] == player):
                count_pos += 1
                r += dr
                c += dc

            # 反向计数
            count_neg = 0
            r, c = row - dr, col - dc
            while (0 <= r < board.size and 0 <= c < board.size and
                   board.board[r][c] == player):
                count_neg += 1
                r -= dr
                c -= dc

            total_count = count_pos + count_neg + 1  # +1 包括当前位置

            # 检查两端是否被阻挡
            pos_blocked = (row + (count_pos + 1) * dr < 0 or
                          row + (count_pos + 1) * dr >= board.size or
                          col + (count_pos + 1) * dc < 0 or
                          col + (count_pos + 1) * dc >= board.size or
                          board.board[row + (count_pos + 1) * dr][col + (count_pos + 1) * dc] != 0)

            neg_blocked = (row - (count_neg + 1) * dr < 0 or
                          row - (count_neg + 1) * dr >= board.size or
                          col - (count_neg + 1) * dc < 0 or
                          col - (count_neg + 1) * dc >= board.size or
                          board.board[row - (count_neg + 1) * dr][col - (count_neg + 1) * dc] != 0)

            # 根据连子数量和阻挡情况评分
            if total_count >= 5:
                total_score += 100000  # 连五
            # 连四：非常高的价值，下一步可能获胜
            elif total_count == 4:
                if not pos_blocked and not neg_blocked:
                    total_score += 50000  # 活四，极高价值
                elif not pos_blocked or not neg_blocked:
                    total_score += 10000  # 冲四，高价值
                else:
                    total_score += 1000   # 双向被堵的四连，仍有价值
            elif total_count == 3:
                if not pos_blocked and not neg_blocked:
                    total_score += 1000   # 活三
                elif not pos_blocked or not neg_blocked:
                    total_score += 500    # 眠三
            elif total_count == 2:
                if not pos_blocked and not neg_blocked:
                    total_score += 100    # 活二
                elif not pos_blocked or not neg_blocked:
                    total_score += 50     # 眠二
            elif total_count == 1:
                if not pos_blocked and not neg_blocked:
                    total_score += 10     # 活一

        return total_score

    def _minimax(self, board, depth, alpha, beta, maximizing_player):
        # Minimax算法with Alpha-Beta剪枝
        if depth == 0:
            return self._evaluate_board(board)

        # 检查游戏是否结束
        if board.last_move:
            row, col = board.last_move
            if board.check_winner(row, col):
                # 如果是机器人获胜（maximizing_player=False轮到机器人下棋的结果）
                if board.board[row][col] == Player.WHITE.value:
                    return 100000
                # 如果是玩家获胜
                else:
                    return -100000

        candidate_moves = self._get_candidate_moves(board)

        if maximizing_player:
            max_eval = float('-inf')
            for row, col in candidate_moves:
                if board.is_valid_move(row, col):
                    board.place_stone(row, col, Player.WHITE.value)
                    eval_score = self._minimax(board, depth - 1, alpha, beta, False)
                    board.undo()

                    max_eval = max(max_eval, eval_score)
                    alpha = max(alpha, eval_score)

                    if beta <= alpha:
                        break  # Beta剪枝
            return max_eval
        else:
            min_eval = float('inf')
            for row, col in candidate_moves:
                if board.is_valid_move(row, col):
                    board.place_stone(row, col, Player.BLACK.value)
                    eval_score = self._minimax(board, depth - 1, alpha, beta, True)
                    board.undo()

                    min_eval = min(min_eval, eval_score)
                    beta = min(beta, eval_score)

                    if beta <= alpha:
                        break  # Alpha剪枝
            return min_eval

    def _evaluate_board(self, board):
        # 局部评估版本 - 使用缓存机制提升性能
        board_hash = self._get_board_hash(board)

        # 检查缓存
        cached_score = self._get_cached_score(board_hash)
        if cached_score is not None:
            return cached_score

        ai_score = 0
        human_score = 0
        evaluated_positions = set()

        # 如果棋盘很空，使用中心区域的局部评估
        piece_count = sum(1 for i in range(board.size) for j in range(board.size) if board.board[i][j] != 0)

        if piece_count < 6:  # 开局阶段：只评估中心5x5区域
            center_start = board.size // 2 - 2
            center_end = board.size // 2 + 3
            for row in range(center_start, center_end):
                for col in range(center_start, center_end):
                    if (0 <= row < board.size and 0 <= col < board.size and
                        board.board[row][col] == 0):
                        evaluated_positions.add((row, col))
        else:  # 中局阶段：评估所有已有棋子周围2格内的空位
            for row in range(board.size):
                for col in range(board.size):
                    if board.board[row][col] != 0:
                        # 检查周围2格范围内的空位
                        for dr in range(-2, 3):
                            for dc in range(-2, 3):
                                new_row, new_col = row + dr, col + dc
                                if (0 <= new_row < board.size and
                                    0 <= new_col < board.size and
                                    board.board[new_row][new_col] == 0):
                                    evaluated_positions.add((new_row, new_col))

        # 评估收集到的关键位置
        for row, col in evaluated_positions:
            # 计算机器人在此位置的价值
            ai_value = self._calculate_pattern_score(board, row, col, Player.WHITE.value)
            ai_score += ai_value

            # 计算人类玩家在此位置的价值
            human_value = self._calculate_pattern_score(board, row, col, Player.BLACK.value)
            human_score += human_value

        # 如果没有找到可评估的位置（极少情况），回退到简单的中心位置评估
        if not evaluated_positions:
            center = board.size // 2
            for row in range(max(0, center-1), min(board.size, center+2)):
                for col in range(max(0, center-1), min(board.size, center+2)):
                    if board.board[row][col] == 0:
                        ai_value = self._calculate_pattern_score(board, row, col, Player.WHITE.value)
                        human_value = self._calculate_pattern_score(board, row, col, Player.BLACK.value)
                        ai_score += ai_value
                        human_score += human_value

        final_score = ai_score - human_score

        # 缓存结果
        self._cache_score(board_hash, final_score)

        return final_score

    def _collect_winning_moves(self, board, player):
        candidate_moves = self._get_candidate_moves(board)
        winning_moves = []

        for row, col in candidate_moves:
            if not board.is_valid_move(row, col):
                continue
            board.place_stone(row, col, player)
            if board.check_winner(row, col):
                winning_moves.append((row, col))
            board.undo()

        return winning_moves

    def _find_winning_move(self, board, player):
        # 找能直接赢的位置
        winning_moves = self._collect_winning_moves(board, player)
        return winning_moves[0] if winning_moves else None

    def _is_winning_move(self, board, row, col, player):
        if not board.is_valid_move(row, col):
            return False

        board.place_stone(row, col, player)
        is_win = board.check_winner(row, col)
        board.undo()
        return is_win

    def _get_llm_move(self, board):
        # 用AI模型决定下哪
        if not self.llm_agent:
            return self._get_hard_move(board)

        immediate_win = self._find_winning_move(board, Player.WHITE.value)
        if immediate_win:
            return immediate_win

        opponent_wins = self._collect_winning_moves(board, Player.BLACK.value)
        if len(opponent_wins) == 1:
            return opponent_wins[0]

        hard_move = self._get_hard_move(board)

        analysis = None
        try:
            analysis = self.llm_agent.analyze(board)
        except Exception as exc:
            print(f"调用大模型失败，回退至困难策略：{exc}")
            analysis = None

        candidate_moves = []
        if analysis:
            candidate_moves.extend(analysis.candidate_moves)
            if analysis.best_move:
                candidate_moves.append(analysis.best_move)

        candidate_moves.extend(opponent_wins)
        if hard_move:
            candidate_moves.append(hard_move)

        candidate_moves = self._deduplicate_moves(candidate_moves)

        verified_move = self._select_verified_move(board, candidate_moves, hard_move)
        if verified_move:
            return verified_move

        return hard_move if hard_move else self._get_medium_move(board)

    def _deduplicate_moves(self, moves):
        seen = set()
        unique_moves = []
        for move in moves:
            if move and move not in seen:
                seen.add(move)
                unique_moves.append(move)
        return unique_moves

    def _select_verified_move(self, board, candidate_moves, fallback):
        best_move = None
        best_score = float('-inf')

        for move in candidate_moves:
            if not board.is_valid_move(*move):
                continue
            score = self._verify_move_strength(board, move)
            if score > best_score:
                best_score = score
                best_move = move

        if best_move:
            return best_move

        if fallback and board.is_valid_move(*fallback):
            return fallback

        return None

    def _verify_move_strength(self, board, move, base_depth=3):
        row, col = move
        if not board.is_valid_move(row, col):
            return float('-inf')

        board.place_stone(row, col, Player.WHITE.value)

        if board.check_winner(row, col):
            board.undo()
            return 200000

        opponent_wins = self._collect_winning_moves(board, Player.BLACK.value)
        if opponent_wins:
            penalty = -120000 - len(opponent_wins) * 5000
            board.undo()
            return penalty

        follow_up_wins = self._collect_winning_moves(board, Player.WHITE.value)
        if len(follow_up_wins) >= 2:
            board.undo()
            return 150000 + len(follow_up_wins) * 2000

        attack_score = self._calculate_pattern_score(board, row, col, Player.WHITE.value)
        defense_score = self._calculate_pattern_score(board, row, col, Player.BLACK.value)

        depth = base_depth
        piece_count = len(board.history)
        if piece_count > 10:
            depth += 1
        if piece_count > 18:
            depth += 1
        if piece_count > 26:
            depth += 1

        if attack_score >= 50000:
            depth += 2
        elif attack_score >= 10000:
            depth += 1
        elif defense_score >= 5000:
            depth += 1

        depth = max(2, min(5, depth))

        search_score = self._minimax(board, depth, float('-inf'), float('inf'), False)
        board.undo()

        return search_score + attack_score * 0.05 - defense_score * 0.01

    def _get_board_hash(self, board):
        # 生成棋盘局面的哈希值，用于缓存索引
        # 使用简单的字符串哈希，对于相同局面应该足够
        board_str = ''.join(str(cell) for row in board.board for cell in row)
        return hash(board_str)

    def _get_cached_score(self, board_hash):
        # 从缓存中获取局面评分
        return self.transposition_table.get(board_hash)

    def _cache_score(self, board_hash, score):
        # 缓存局面评分
        # 如果缓存过大，清理一部分
        if len(self.transposition_table) > self.cache_size_limit:
            # 简单策略：清理一半的缓存
            keys_to_remove = list(self.transposition_table.keys())[:self.cache_size_limit // 2]
            for key in keys_to_remove:
                del self.transposition_table[key]

        self.transposition_table[board_hash] = score

    def _clear_cache(self):
        # 清空缓存（可选，用于新游戏开始时）
        self.transposition_table.clear()
